﻿using RockBus.Configuration;
using RockBus.DataContracts;
using RockBus.Logging.Interfaces;
using System;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;

namespace RockBus.ServiceBus
{
    public class PublisherClient
    {
        public PublisherClient(ILogger logger, EndpointInfo rockBusEndpoint = null)
        {
            this.Logger = logger;
            this.ServiceBusEnvironment = new ServiceBusEnvironment(logger, rockBusEndpoint);
        }

        public ILogger Logger { get; private set; }

        public PublisherClient(ILogger logger, string publisherId, EndpointInfo rockBusEndpoint = null)
            : this(logger, rockBusEndpoint)
        {
            if ((null == ServiceBusClientConfigurationSection.Instance) || (null == ServiceBusClientConfigurationSection.Instance.Publishers))
            {
                throw new ConfigurationErrorsException("The ServiceBus Publisher(s) were not configured. Pls. refer to RockBus.Configuration.ServiceBusClientConfigurationSection for the configuration schemas and documentation.");
            }

            var pc = ServiceBusClientConfigurationSection.Instance.Publishers.CreatePublisherConfiguration();
            this.PublisherInfo = pc.EvaluationElementDictionary[publisherId];
        }

        public PublisherClient(ILogger logger, PublisherInfo publisherInfo, EndpointInfo rockBusEndpoint = null)
            : this(logger, rockBusEndpoint)
        {
            this.PublisherInfo = publisherInfo;
        }

        private ServiceBusEnvironment ServiceBusEnvironment { get; set; }

        private ClientTransport ClientTransport { get; set; }

        public PublisherInfo PublisherInfo { get; private set; }

        public Task PublishMessageAsync<TRequest>(TRequest request, string topic = null)
        {
            return this.ClientTransport.SendMessageAsync(request, topic: topic);
        }

        public Task<TResponse> PublishRequestAsync<TRequest, TResponse>(TRequest request, string topic = null)
        {
            return this.ClientTransport.SendRequestAsync<TRequest, TResponse>(request, topic: topic);
        }

        public Task PublishMessageAsync(XmlReader requestReader, string action, string topic = null)
        {
            return this.ClientTransport.SendMessageAsync(requestReader, action, topic);
        }

        public Task<XmlReader> PublishRequestAsync(XmlReader requestReader, string action, string topic = null)
        {
            return this.ClientTransport.SendRequestAsync(requestReader, action, topic);
        }

        private int _guard;

        public async Task ConfigureAsync()
        {
            if (this.PublisherInfo == null)
            {
                return;
            }

            if (Interlocked.CompareExchange(ref this._guard, 1, 0) == 1)
            {
                return;
            }

            var request = new AddOrUpdatePublishersRequest(this.PublisherInfo);
            var response = await this.ServiceBusEnvironment.AddOrUpdatePublishersWithResponseAsync(request);

            // Overwrite the relative endpoins with the absolute endpoints, as provided by RockBus
            foreach (var endpoint in response.Endpoints)
            {
                this.PublisherInfo.EndpointInfoDictionary[endpoint.Id] = endpoint;
            }

            var publishEndpoint = this.PublisherInfo.EndpointInfoDictionary.Values.FirstOrDefault();
            if (publishEndpoint == null)
            {
                publishEndpoint = this.ServiceBusEnvironment.GetRockBusEndpoint();
            }

            this.ClientTransport = new ClientTransport(this.Logger, publishEndpoint);
        }

        public Task UnconfigureAsync()
        {
            if (this.PublisherInfo == null)
            {
                return Task.FromResult<bool>(true);
            }

            if (Interlocked.CompareExchange(ref this._guard, 0, 1) == 0)
            {
                return Task.FromResult<bool>(true);
            }

            var request = new RemovePublishersRequest(this.PublisherInfo.Id);
            return this.ServiceBusEnvironment.RemovePublishersAsync(request);
        }
    }
}